package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"net/http"
	"os"
	"strings"
	"time"
	"zbn-oil-consumption/config"
	"zbn-oil-consumption/models"
	"zbn-oil-consumption/utils"
)

type Claims struct {
	UserId   int64  `json:"userId"`
	Username string `json:"Username"`
	Nickname string `json:"nickname"`
	jwt.RegisteredClaims
}

type UserToken struct {
	UserId    int64     `json:"userId"`
	Username  string    `json:"username"`
	Nickname  string    `json:"nickname"`
	Token     string    `json:"token"`
	CreatedAt time.Time `json:"createdAt"`
}

func Auth() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")
		if strings.TrimSpace(token) == "" {
			c.Abort()
			utils.Fail(c, http.StatusUnauthorized, "token为空")
			return
		}
		token = strings.TrimPrefix(token, "Bearer ")
		claims, err := ParseToken(token)
		if err != nil {
			c.Abort()
			utils.Fail(c, http.StatusUnauthorized, "token失效")
			return
		}
		if claims.UserId == 0 {
			c.Abort()
			utils.Fail(c, http.StatusUnauthorized, "token缺失用户信息")
			return
		}

		//验证tokenVersion
		c.Set("userId", claims.UserId)
		c.Set("username", claims.Username)
		c.Next()
	}
}

// ParseToken 验证解析token
func ParseToken(token string) (*Claims, error) {
	var err error
	jwtToken, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (i interface{}, e error) {
		// 验证签名算法
		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		publicKey, err := os.ReadFile(config.Cfg.Cert.PublicKeyPath)
		key, err := jwt.ParseRSAPublicKeyFromPEM(publicKey)
		if err != nil {
			return nil, err
		}
		return key, nil
	})
	if err == nil && jwtToken != nil {
		if claim, ok := jwtToken.Claims.(*Claims); ok && jwtToken.Valid {
			return claim, nil
		}
	}
	return nil, err
}

// GenerateTokenPair 生成token对
func GenerateTokenPair(user *models.User) (tokenPair *UserToken, err error) {
	tokenPair = &UserToken{}
	privateKeyPem, err := os.ReadFile(config.Cfg.Cert.PrivateKeyPath)
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyPem)
	if err != nil {
		return
	}
	// 创建Claims
	accessTokenClaims := &Claims{
		UserId:   user.Id,
		Username: user.Username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(24) * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "zbn",
			Subject:   user.Username,
		},
	}
	// 生成accessToken
	aToken := jwt.NewWithClaims(jwt.SigningMethodRS256, accessTokenClaims)
	// 签名token
	tokenPair.Token, err = aToken.SignedString(privateKey)
	if err != nil {
		fmt.Println("签名accessToken失败", err.Error())
		return
	}
	tokenPair.UserId = user.Id
	tokenPair.Username = user.Username
	tokenPair.Nickname = user.Nickname
	tokenPair.CreatedAt = user.CreatedAt
	return
}

func GenerateAdminTokenPair(user *models.Admin) (tokenPair *UserToken, err error) {
	tokenPair = &UserToken{}
	privateKeyPem, err := os.ReadFile(config.Cfg.Cert.PrivateKeyPath)
	privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyPem)
	if err != nil {
		return
	}
	// 创建Claims
	accessTokenClaims := &Claims{
		UserId:   user.Id,
		Username: user.Username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(24) * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "zbn",
			Subject:   user.Username,
		},
	}
	// 生成accessToken
	aToken := jwt.NewWithClaims(jwt.SigningMethodRS256, accessTokenClaims)
	// 签名token
	tokenPair.Token, err = aToken.SignedString(privateKey)
	if err != nil {
		fmt.Println("签名accessToken失败", err.Error())
		return
	}
	tokenPair.UserId = user.Id
	tokenPair.Username = user.Username
	tokenPair.Nickname = user.Nickname
	return
}
